from django.http import HttpResponseForbidden
from django.shortcuts import render, redirect, reverse
from django.views import View
from django import http
import re
from django.core.cache import cache
from apps.areas.models import Area, Address
from apps.carts.utils import merge_cart_cookie_to_redis
from apps.goods.models import SKU
from apps.users.models import User
from meiduo_mall.settings.dev import LOGGER
from django.contrib.auth import login, logout
from utils.response_code import RETCODE
from django_redis import get_redis_connection
from django.contrib.auth import authenticate
from django.contrib.auth.mixins import LoginRequiredMixin
import json
from django.conf import settings
from django.core.mail import send_mail
from celery_tasks.email.tasks import send_verify_email
from utils.secret import SecretOauth
from . import constants
# Create your views here.


class RegisterView(View):
    """注册类视图"""
    def get(self, request):
        """GET 返回注册页面"""

        return render(request, "register.html")

    def post(self, request):
        """POST 注册信息校验"""
        # 获取用户填写的数据
        username = request.POST.get("username")
        password = request.POST.get("password")
        password2 = request.POST.get("password2")
        mobile = request.POST.get("mobile")
        allow = request.POST.get("allow")
        msg_code = request.POST.get("msg_code")

        # 进行判空操作
        if not all([username, password, password2, mobile, allow]):
            return http.HttpResponseForbidden("缺少必填参数!")
        # 数据校验
        if not re.match(r"^[a-zA-Z0-9_-]{5,20}$", username):
            return http.HttpResponseForbidden("用户名不合法啊!")

        if not re.match(r"^[0-9A-Za-z]{8,20}$", password):
            return http.HttpResponseForbidden("密码不合法啊!")

        if password2 != password:
            return http.HttpResponseForbidden("两次密码不一致!")

        if not re.match(r"^1[345789]\d{9}$", mobile):
            return http.HttpResponseForbidden("手机号不合法啊!")

        if allow != "on":
            return http.HttpResponseForbidden("请勾选协议!")

        # 进行手机验证码校验
        # sms_cli = get_redis_connection("sms_code")
        # redis_sms_code = sms_cli.get("sms_code_%s" % mobile)
        # try:
        #     sms_cli.delete("sms_code_%s" % mobile)
        # except Exception as e:
        #     LOGGER.error(e)
        #
        # if redis_sms_code is None:
        #     return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})
        #
        # if msg_code != redis_sms_code.decode():
        #     return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})

        # 成功校验进行用户存储
        try:
            # 防止数据库操作出错
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except Exception as e:
            LOGGER.info(e)
            return render(request, "register.html", {'register_errmsg': '注册失败'})

        # 会话保持
        login(request, user)

        # 响应注册结果
        response = redirect(reverse('Contents:index'))

        # 注册时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

        return response


class UsernameCountView(View):
    """用户名唯一性校验接口"""
    def get(self, request, username):

        count = User.objects.filter(username=username).count()

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


class MobilesCountView(View):
    """手机号唯一性校验"""
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({"code": RETCODE.OK, 'errmsg': 'OK', "count": count})


class LoginView(View):
    def get(self, request):
        """登陆页面"""
        return render(request, "login.html")

    def post(self, request):
        username = request.POST.get("username")
        password = request.POST.get("pwd")
        remembered = request.POST.get("remembered")

        if not all([username, password]):
            return http.HttpResponseForbidden("缺少必填参数!")

        if not re.match(r"^[a-zA-Z0-9_-]{5,20}$", username):
            return http.HttpResponseForbidden("用户名不合法啊!")

        if not re.match(r"^[0-9A-Za-z]{8,20}$", password):
            return http.HttpResponseForbidden("密码不合法啊!")

        # 通过 authenticate 校验用户账户密码 返回值 user 对象
        user = authenticate(request, username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})

        # 保持登录状态
        login(request, user)

        # 是否记住用户名
        if remembered != 'on':
            # 不记住用户名, 浏览器结束会话就过期
            request.session.set_expiry(0)
        else:
            # 记住用户名, 浏览器会话保持两周
            request.session.set_expiry(None)

        # 响应登陆结果
        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('Contents:index'))

        # 合并购物车
        response = merge_cart_cookie_to_redis(request, response)
        # 注册时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
        return response


class LogOutView(View):
    """退出登录"""

    def get(self, request):
        """实现退出登录逻辑"""
        # 清理session
        logout(request)
        # 退出登录，重定向到登录页
        response = redirect(reverse('Contents:index'))
        # 退出登录时清除cookie中的username
        response.delete_cookie('username')

        return response


class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):
        """用户中心"""
        context = {
            "username": request.user.username,
            "mobile": request.user.mobile,
            "email": request.user.email,
            "email_active": request.user.email_active,
        }
        return render(request, "user_center_info.html", context)

    # LoginRequiredMixin 判断用户是否登陆
    # 如果通过登录验证则返回True。反之，返回False


class EmailView(LoginRequiredMixin, View):
    def put(self, request):
        """添加邮箱"""
        email = json.loads(request.body.decode()).get("email")

        # 校验参数
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('参数email有误')

        # 赋值email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})

        data_dict = {'user_id': request.user.id, "email": email}
        # 加密
        secret_dict = SecretOauth().dumps(data_dict)
        verify_url = settings.EMAIL_ACTIVE_URL + "?token=" + secret_dict
        # # 标准发送邮件 失败
        # # 邮件标题
        # subject = "美多商城邮箱验证"
        # # 验证链接
        # verify_url = settings.EMAIL_ACTIVE_URL + "?token=" + json.dumps(data_dict)
        # html_message = '<p>尊敬的用户您好！</p>' \
        #                '<p>感谢您使用美多商城。</p>' \
        #                '<p>您的邮箱为：%s 。请点击此链接激活您的邮箱：</p>' \
        #                '<p><a href="%s">%s<a></p>' % (email, verify_url, verify_url)
        # result = send_mail(subject, "", settings.EMAIL_FROM, [email], html_message)
        # print(result)

        # 异步发送 成功
        send_verify_email.delay(email, verify_url)
        # 响应添加邮箱结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})


def check_verify_email_token(token):
    """邮件激活 token 解密"""
    try:
        # 存在解析 该加解密方式 原有类型不变
        token_dict = SecretOauth().loads(token)
    except Exception as e:
        LOGGER.error(e)
        return None

    try:
        # 根据 token 中的 user id 与 email 确定用户并修改数据库用户邮箱状态
        user = User.objects.get(id=token_dict['user_id'], email=token_dict['email'])
    except Exception as e:
        LOGGER.error(e)
        return None
    else:
        return user


class VerifyEmailView(View):
    def get(self, request):
        # 解析查询参数
        token = request.GET.get("token")

        # 校验参数：判断token是否为空和过期，提取user
        if not token:
            return http.HttpResponseBadRequest('缺少token')

        user = check_verify_email_token(token)

        try:
            # 查到用户之后 根据查询到的 user 修改邮箱状态
            user.email_active = True
            # 对象属性修改切记保存
            user.save()
        except Exception as e:
            LOGGER.error(e)
            return http.HttpResponseServerError('激活邮件失败')

        # 返回邮箱验证结果
        return redirect(reverse('Users:info'))


class AreasView(LoginRequiredMixin, View):
    def get(self, request):
        """提供省市县数据"""
        area_id = request.GET.get("area_id")

        if not area_id:
            # 如果没有 area_id 查询的是省信息
            # 减少 io 操作 使用 django 缓存解决问题
            # 读取 省份缓存
            province_list = cache.get('province_list')
            if not province_list:
                try:
                    # 查询 省份数据
                    province_model_list = Area.objects.filter(parent__isnull=True)

                    # 序列化并构造 前端 响应数据
                    province_list = []
                    for province_model in province_model_list:
                        province_list.append({'id': province_model.id, 'name': province_model.name})
                except Exception as e:
                    LOGGER.error(e)
                    return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '省份数据错误'})

            # 存储省份缓存数据
            cache.set('province_list', province_list, 3600)
            # 响应省份数据
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'province_list': province_list})

        else:
            # 如果存在 area_id 查询的是市县信息
            # 读取市或区缓存数据
            sub_data = cache.get('sub_area_' + area_id)
            if not sub_data:
                try:
                    # 查询市或区的父级
                    parent_model = Area.objects.get(id=area_id)
                    sub_model_list = parent_model.subs.all()
                    # 序列化市或区数据
                    sub_list = []
                    for sub_model in sub_model_list:
                        sub_list.append({'id': sub_model.id, 'name': sub_model.name})
                    sub_data = {
                        'id': parent_model.id,  # 父级pk
                        'name': parent_model.name,  # 父级name
                        'subs': sub_list  # 父级的子集
                    }
                except Exception as e:
                    LOGGER.error(e)
                    return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '城市或区数据错误'})
            # 存储省份缓存数据
            cache.set('sub_area_' + area_id, sub_data, 3600)
            # 响应市或区数据
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'sub_data': sub_data})


class CreateAddressesView(LoginRequiredMixin, View):
    def post(self, request):
        """增加收货地址"""
        # 判断是否超过地址上限：最多20个
        count = Address.objects.filter(user=request.user, is_deleted=False).count()
        # count = request.user.addresses.count()
        if count >= constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})

        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验字段
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 存储字段
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        # 设置默认地址
        if not request.user.default_address:
            request.user.default_address = address
            request.user.save()
        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应保存结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


class AddressView(LoginRequiredMixin, View):
    def get(self, request):
        """展示用户地址"""
        # 查询当前用户所有地址
        addresses = Address.objects.filter(user=request.user, is_deleted=False)

        # 构造前端响应数据格式
        address_dict_list = []
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)
        context = {
            'default_address_id': request.user.default_address_id,
            'addresses': address_dict_list,
        }
        return render(request, "user_center_site.html", context)


class UpdateDestroyAddressView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        """修改地址"""
        # 接收修改的参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 判断地址是否存在,并根据id更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})

        # 构造响应数据 更新数据之后需要重新获取一次对象
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应更新地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功', 'address': address_dict})

    def delete(self, request, address_id):
        """删除地址"""
        # 逻辑删除
        try:
            Address.objects.filter(id=address_id).update(is_deleted=True)
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        # 响应删除地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})


class DefaultAddressView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        """设置默认地址"""
        try:
            # address = Address.objects.filter(id=address_id)
            # User.objects.get(user=request.user).update(default_address=address)
            address = Address.objects.get(id=address_id)
            # 设置地址为默认地址
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})

        # 响应设置默认地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


class UpdateTitleAddressView(LoginRequiredMixin, View):
    """设置地址标题"""
    def put(self, request, address_id):
        """设置地址标题"""
        # 接收参数：地址标题
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')
        try:
            Address.objects.filter(id=address_id).update(title=title)
        except Exception as e:
            LOGGER.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})

        # 4.响应删除地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})


class ChangePasswordView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, "user_center_pass.html")

    def post(self, request):
        """修改密码"""
        # 接收参数
        old_password = request.POST.get('old_pwd')
        new_password = request.POST.get('new_pwd')
        new_password2 = request.POST.get('new_cpwd')

        # 校验参数
        if not all([old_password, new_password, new_password2]):
            return http.HttpResponseForbidden('缺少必传参数')

        # 校验原始密码
        result = request.user.check_password(old_password)
        if not result:
            LOGGER.error("[修改密码]原始密码错误")
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '原始密码错误'})

        # 校验新密码格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return http.HttpResponseForbidden('密码最少8位，最长20位')
        if new_password != new_password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')

        # 修改密码 set_password
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            LOGGER.error(e)
            return render(request, 'user_center_pass.html', {'change_pwd_errmsg': '修改密码失败'})

        # 修改成功 清除状态 并退出登陆
        logout(request)
        response = redirect(reverse('Users:login'))
        response.delete_cookie('username')

        # 响应密码修改结果：重定向到登录界面
        return response


class UserBrowseHistory(LoginRequiredMixin, View):
    """用户浏览记录"""

    def post(self, request):
        """保存用户浏览记录"""
        # 1.接收json参数
        sku_id = json.loads(request.body.decode()).get('sku_id')

        # 2.根据sku_id 查询sku
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseForbidden('商品不存在!')

        # 3.如果有sku,保存到redis
        history_redis_client = get_redis_connection('history')
        history_key = 'history_%s' % request.user.id

        redis_pipeline = history_redis_client.pipeline()
        # 3.1 去重
        history_redis_client.lrem(history_key, 0, sku_id)
        # 3.2 存储
        history_redis_client.lpush(history_key, sku_id)
        # 3.3 截取 5个
        history_redis_client.ltrim(history_key, 0, 4)
        redis_pipeline.execute()

        # 响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

    def get(self, request):
        """获取用户浏览记录"""
        # 获取Redis存储的sku_id列表信息
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % request.user.id, 0, -1)

        # 根据sku_ids列表数据，查询出商品sku信息
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})


class FindPasswordView(View):
    """忘记密码"""
    def get(self, request):
        """参数获取"""
        return render(request, "find_password.html")




